package com.android.vmg;

import java.util.ArrayList;
import java.util.List;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.SurfaceHolder;

class GameThread extends Thread {
	
	// States
	
	public static final int PAUSED = 0;
	public static final int READY = 1;
	public static final int RUNNING = 2;
	public static final int LOSE = 3;
	public static final int WIN = 4;

	// Physical Attributes
	
	private int mCanvasHeight = 1;
	private int mCanvasWidth = 1;
	
	// Attributes

	private int circleRadius = 30;

	// Constants
	
	private Paint circlePaint;
	private Paint textPaint;
	
	// Parameters
	
	private boolean showText;
	private long startTime;
	private long actualTime;
	private long showDelay = 2000;

	// Virtual
	
	private SurfaceHolder mSurfaceHolder;
	private GameView mGameView;
	
	private List<Circle> mCircles;
	
	// Meta
	
	private boolean mRun;
	private int mState;
	private Game.GameListener mListener;
	private int numCircles;

	public GameThread(SurfaceHolder surfaceHolder, 
			GameView gameView) {
		
		mSurfaceHolder = surfaceHolder;
		mGameView = gameView;
		
		mCircles = new ArrayList<Circle>();
					
        circlePaint = new Paint();
        circlePaint.setAntiAlias(true);
        circlePaint.setColor(Color.BLACK);
        
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setColor(Color.WHITE);
	}
	
	public void setListener(Game.GameListener listener) {
		mListener = listener;
	}
	
	private void calculateConstants() {
		if(!mCircles.isEmpty()) {
			int nCircles = mCircles.size();
			int area = mCanvasHeight * mCanvasWidth;
			double proporcionalArea = area / nCircles;
			double squareSide = Math.sqrt(proporcionalArea);
			double GAP = 0.8;
			circleRadius = (int)(GAP * (squareSide / 2));
			if(nCircles > 0) {
				Circle m = mCircles.get(0);
				for(Circle c : mCircles) {
					if(m.getVal() < c.getVal())
						m = c;
				}
				textPaint.setTextSize((circleRadius)/(new Integer(m.getVal()).toString().length()));
			}
		}
	}
			
	public void setRunning(boolean v) {
		synchronized(mSurfaceHolder) {
			mRun = v;
		}
	}
	
	public synchronized void restoreState(Bundle savedState) {
		synchronized(mSurfaceHolder) {
			if(savedState != null) {
			}
		}
	}
	
	public Bundle saveState(Bundle map) {
		synchronized(mSurfaceHolder) {
			if(map != null) {
			}
		}
		return map;
	}
	
	public boolean textIsVisible() {
		return showText;
	}
	
    @Override
    public void run() {
    	startTime = System.currentTimeMillis();
    	showText = true;
        Canvas canvas = null;
        while (mRun) {
        	update();
            try {
                canvas = mSurfaceHolder.lockCanvas();
                synchronized (mSurfaceHolder) {
                	doDraw(canvas);
                }
            } finally {
                if (canvas != null) {
                    mSurfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
        mListener.gameEnd();
    }
    
    private void update() {
        if(showText) {
            actualTime = System.currentTimeMillis();
            long gap = actualTime - startTime;
            if(gap > showDelay) {
            	showText = false;
            }
        }
        if(mState == WIN || mState == LOSE) {
/*        	synchronized(mSurfaceHolder) {
	        	mListener.gameEnd();
	        }*/
        	setRunning(false);
        }
    }
    
    public int gameState() {
    	return mState;
    }
    
    public void doDraw(Canvas canvas) {
		canvas.drawColor(Color.WHITE);
    	for(Circle circle : mCircles) {
    		canvas.drawCircle(circle.getX(), circle.getY(), circle.getRadius(), circlePaint);
    		if(showText)
    			canvas.drawText(new String() + circle.getVal(), circle.getX() - (circle.getRadius()/2), circle.getY() + (circle.getRadius()/2), textPaint);
    	}
    	canvas.save();
    	canvas.restore();
    }
    
    public void setState(int state) {
    	synchronized(mSurfaceHolder) {
    		mState = state;
    	}
    }
    
    public void setSurfaceSize(int width, int height) {
    	synchronized(mSurfaceHolder) {
    		mCanvasWidth = width;
    		mCanvasHeight = height;
    		calculateConstants();
    		scramble();
    	}
    }
    
    private void scramble() {
    	if(!mCircles.isEmpty()) {
        	synchronized(mSurfaceHolder) {
        		List<Integer> vals = extractVals(mCircles);
        		List<Circle> circles = generateCircles(vals);
        		setCircles(circles);
        		showText = true;
        	}
    	}
    }
    
    private List<Integer> extractVals(List<Circle> circles) {
    	List<Integer> vals = new ArrayList<Integer>();
    	for(Circle c : circles) {
    		vals.add(c.getVal());
    	}
    	return vals;
    }
    
    private int nextSquare(int n) {
    	int a = n;
    	while(!perfectSquare(a))
    		a++;
    	return (int)Math.sqrt(a) + 1;
    }
    
    private boolean perfectSquare(int n) {
    	double sq = Math.sqrt(n);
    	return (int)sq == sq;
    }
    
    private List<Point> generatePointsGrid(int n, int minWidth, int width, int minHeight, int height) {
    	List<Point> points = new ArrayList<Point>();
    	int sq = nextSquare(n);
    	double widthGap = (width - minWidth) / sq;
    	double heightGap = (height - minHeight) / sq;
    	for(int i = 0; i < sq; i++) {
    		for(int j = 0; j < sq; j++) {
    			points.add(new Point((int)(widthGap*i + widthGap/2),
    					(int)(heightGap*j + heightGap/2)));
    		}
    	}
    	while(points.size() > n)
    		points.remove((int)(Math.random() * (n-1)));
    	return points;
    }
    
    private List<Circle> generateCircles(List<Integer> vals) {
    	List<Circle> circles = new ArrayList<Circle>();
    	List<Point> points = generatePointsGrid(vals.size(), 0, mCanvasWidth, 0, mCanvasHeight);
    	for(int i = 0; i < vals.size(); i++) {
    		circles.add(new Circle(vals.get(i), points.get(i).x, points.get(i).y, circleRadius));
    	}
    	return circles;
    }
    
    private List<Integer> generateVals(int n, int min, int max) {
    	List<Integer> vals = new ArrayList<Integer>();
    	for(int i = 0; i < n; i++) {
    		int c = min + (int)(Math.random() * (max - min));
    		while(vals.contains(c))
    			c = min + (int)(Math.random() * (max - min));
    		vals.add(c);
    	}
    	return vals;
    }
    
    public void populate(int n, int minRange, int maxRange) {
    	synchronized(mSurfaceHolder) {
        	setCircles(generateCircles(generateVals(n, minRange, maxRange)));
    	}
    }
    
    public void addCircle(Circle c) {
    	synchronized(mSurfaceHolder) {
    		mCircles.add(c);
    		calculateConstants();
    		scramble();
    	}
    }
    
    public void doTouch(int x, int y) {
		if(!textIsVisible()) {
//	    	synchronized(mSurfaceHolder) {
	    		Circle c = detectCollide(x, y);
	    		if(c != null) {
	    			removeCircle(c);
	    		}
//	    	}
		}
    }
    
    private void removeCircle(Circle c) {
    	synchronized(mSurfaceHolder) {
        	if(mCircles.size() == 1) {
        		setState(WIN);
            	mCircles.remove(c);
        	} else {
            	int min = mCircles.get(0).getVal();
            	for(Circle circle : mCircles)
            		if(circle.getVal() < min)
            			min = circle.getVal();
            	if(c.getVal() != min) {
            		setState(LOSE);
            	} else
                	mCircles.remove(c);
        	}
    	}
    }
    
    private Circle detectCollide(int x, int y) {
    	for(Circle c : mCircles) {
    		if(c.collide(x, y))
    			return c;
    	}
    	return null;
    }
    
    public void pause() {
    	mState = PAUSED;
    }
    
    public void unpause() {
    	mState = RUNNING;
    }
    
    public int getRoundNumCircles() {
    	return numCircles;
    }
    
    public int getNumCircles() {
    	return mCircles.size();
    }
    
    public int getPoints() {
    	return getRoundNumCircles() - getNumCircles();
    }
    
    private void setCircles(List<Circle> circles) {
    	synchronized(mSurfaceHolder) {
        	mCircles = circles;
        	numCircles = mCircles.size();
        	calculateConstants();
    	}
    }
}